﻿using System;
using System.Collections.Generic;

namespace WindowsPhone.CompactContainer.Aggregator
{
    internal class EventHandlers
    {
        internal EventHandlers()
        {
            Handlers = new Dictionary<Type, IList<EventHandlerOptions>>();
            HandlersCleanUp = new Dictionary<object, IList<Action>>();
        }

        internal void Add<T>(EventHandlerOptions handler)
        {
            Type eventType = typeof(T);
            Add(eventType, handler);
        }

        internal void Add(Type eventType, EventHandlerOptions handler)
        {
            if (!Handlers.ContainsKey(eventType))
            {
                Handlers[eventType] = new List<EventHandlerOptions>();
            }
            Handlers[eventType].Add(handler);
            
            // register cleanup
            if (handler.EventHandler!=null)
            {
                if (!HandlersCleanUp.ContainsKey(handler.EventHandler))
                {
                    HandlersCleanUp[handler.EventHandler] = new List<Action>();
                }
                HandlersCleanUp[handler.EventHandler].Add(() => Handlers[eventType].Remove(handler));
            }
        }

        private IEnumerable<EventHandlerOptions> GetEventHandlers<T>()
        {
            Type key = typeof(T);
            IList<EventHandlerOptions> list = new List<EventHandlerOptions>();
            if (this.Handlers.ContainsKey(key))
            {
                foreach (var options in Handlers[key])
                {
                    if (options != null)
                    {
                        list.Add(options);
                    }
                }
            }
            return list;
        }

        internal void Handle<T>(T eventData)
        {
            var eventHandlers = GetEventHandlers<T>();
            foreach (var options in eventHandlers)
            {
                try
                {
                    var eventHandler = options.EventHandler as IEventHandler<T>;
                    if (eventHandler != null)
                    {
                        eventHandler.Handle(eventData);
                    }
                }
                catch (Exception exception)
                {
                    HandleError(options, exception);
                }
            }
        }

        private void HandleError(EventHandlerOptions eventhandlerOptions, Exception ex)
        {
            if (ErrorHandler != null)
            {
                ErrorHandler(ex);
            }
            if (eventhandlerOptions.ErrorHandler != null)
            {
                eventhandlerOptions.ErrorHandler(ex);
            }
        }

        internal void OnHandlerError(Action<Exception> errorHandler)
        {
            ErrorHandler = errorHandler;
        }

        internal void Remove(object handlerInstance)
        {
            if(HandlersCleanUp.ContainsKey(handlerInstance))
            {
                foreach (var cleanUp in HandlersCleanUp[handlerInstance])
                {
                    cleanUp();
                }
                HandlersCleanUp.Remove(handlerInstance);
            }
        }

        private Action<Exception> ErrorHandler { get; set; }

        private IDictionary<Type, IList<EventHandlerOptions>> Handlers { get; set; }

        private IDictionary<object, IList<Action>> HandlersCleanUp { get; set; }

    }
}
